package com.relytosoft.mizar.media.camera.services.service.impl;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.relytosoft.dubhe.common.core.base.page.PageQuery;
import com.relytosoft.dubhe.common.core.utils.BeanCopyUtils;
import com.relytosoft.dubhe.common.core.utils.TreeBuildUtils;
import com.relytosoft.mizar.media.camera.api.entity.CameraBase;
import com.relytosoft.mizar.media.camera.api.entity.CameraChannel;
import com.relytosoft.mizar.media.camera.api.entity.CameraPlatform;
import com.relytosoft.mizar.media.camera.api.pojo.dto.CameraBaseDto;
import com.relytosoft.mizar.media.camera.api.pojo.dto.CameraBaseWithPlatformDto;
import com.relytosoft.mizar.media.camera.api.pojo.vo.CameraBasePlatform;
import com.relytosoft.mizar.media.camera.api.pojo.vo.CameraBaseVo;
import com.relytosoft.mizar.media.camera.api.pojo.vo.CameraDistrictVo;
import com.relytosoft.mizar.media.camera.api.pojo.vo.SysDistrictVo;
import com.relytosoft.mizar.media.camera.services.mapper.CameraBaseMapper;
import com.relytosoft.mizar.media.camera.services.mapper.CameraChannelMapper;
import com.relytosoft.mizar.media.camera.services.service.CameraBaseService;
import com.relytosoft.mizar.media.camera.services.service.CameraPlatformService;
import com.relytosoft.mizar.media.camera.services.utils.FeignInResultHandleUtils;
import com.relytosoft.mizar.media.camera.services.utils.FeignInSystemUtils;
import com.relytosoft.mizar.media.common.constant.MediaMqConstants;
import com.relytosoft.mizar.media.common.emqx.client.MqttOp;
import com.relytosoft.mizar.media.common.utils.AesUtils;
import com.relytosoft.mizar.media.system.api.feign.SystemFeignInService;
import com.relytosoft.mizar.media.system.api.pojo.dto.DistrictFeignDto;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;


/**
 * @author lj
 */
@Service
public class CameraBaseServiceImpl extends ServiceImpl<CameraBaseMapper, CameraBase> implements CameraBaseService {

    @Resource
    private CameraChannelMapper cameraChannelMapper;

    @Resource
    private SystemFeignInService systemFeignInService;

    @Resource
    private CameraPlatformService cameraPlatformService;


    @Override
    public Map<String, Integer> findCameraCode() {
        List<CameraBasePlatform> cameraCode = this.baseMapper.findCameraCode(null, 1);
        return cameraCode.stream().collect(Collectors.toMap(CameraBasePlatform::getPlatformCameraCode, CameraBasePlatform::getId));
    }

    @Override
    public void regionEncodeDevice(Integer type) {
        //1. 远程调用查询本系统所有区域编码regionIndexCode
        List<DistrictFeignDto> feignDTOList = FeignInResultHandleUtils.handlerResponse(systemFeignInService.selectDistrictList());
        //2. 通知远程调用海康数据查询
        if (feignDTOList != null && feignDTOList.size() > 0) {
            JSONObject jsonObject = new JSONObject();
            HashMap<String, String> map = new HashMap<>(20);
            map.put("feignDTOList", JSON.toJSONString(feignDTOList));
            map.put("isUpdate", type == 0 ? "false" : "true");
            jsonObject.put("OpType", "SELECT_HK_ENCODE");
            jsonObject.put("Data", map);
            MqttOp.publish(MediaMqConstants.CAMERA_TOPIC, JSON.toJSONString(jsonObject));
        }
    }


    @Override
    public IPage<CameraBaseVo> selectCameraPageList(CameraBaseDto cameraBaseDto, PageQuery pageQuery) {
        CameraBaseVo cameraBaseVo = BeanCopyUtils.copy(cameraBaseDto, CameraBaseVo.class);
        //如果未指定排序默认根据id排序
        if (StrUtil.isBlank(pageQuery.getOrderByColumn())) {
            pageQuery.setOrderByColumn("id");
        }
        Integer districtId = cameraBaseVo.getDistrictId();
        Page<CameraBaseVo> page = pageQuery.build();
        ArrayList<Integer> list = new ArrayList<>();
        List<DistrictFeignDto> allArea = FeignInResultHandleUtils.handlerResponse(systemFeignInService.selectDistrictList());
        IPage<CameraBaseVo> iPage;
        //获取该区域下本身和所有子节点
        if (districtId != null && allArea != null && allArea.size() > 0) {
            List<Integer> areaIds = FeignInSystemUtils.findAreaAllChildren(cameraBaseVo.getDistrictId(), list, allArea);
            iPage = this.baseMapper.selectCameraPageList(page, cameraBaseVo, areaIds);
        } else {
            iPage = this.baseMapper.selectCameraPageList(page, cameraBaseVo, list);
        }
        //配置区域完整路径
        setArea(iPage.getRecords(), allArea);
        return iPage;
    }

    @Override
    public boolean updateCamera(CameraBase cameraBase) {
        return this.updateById(cameraBase);
    }

    @Override
    public CameraBaseVo findById(Integer id) {
        //1. 远程查询所有区域信息
        List<DistrictFeignDto> remoteAllArea = FeignInResultHandleUtils.handlerResponse(systemFeignInService.selectDistrictList());
        CameraBaseVo baseVo = this.baseMapper.findById(id);
        if (baseVo != null) {
            baseVo.setDistrictName(FeignInSystemUtils.completeRoute(baseVo.getDistrictId(), remoteAllArea));
            baseVo.setCameraChannelVos(cameraChannelMapper.findById(id));
        }
        return baseVo;
    }


    @Override
    public List<SysDistrictVo> findAreaTree() {
        //1. 远程查询所有区域信息
        List<DistrictFeignDto> remoteAllArea = FeignInResultHandleUtils.handlerResponse(systemFeignInService.selectDistrictList());
        //2. 返回前端的所需数据
        List<SysDistrictVo> all = new ArrayList<>();

        if (remoteAllArea != null && remoteAllArea.size() > 0) {
            //变为需要返回前端得数据
            convertData(remoteAllArea, all);
            //3. 收集顶级父类
            List<SysDistrictVo> sysDistrictVoList = all.stream()
                    .filter(item -> item.getParentId() == null)
                    .collect(Collectors.toList());
            //4. 递归获取子集
            for (SysDistrictVo root : sysDistrictVoList) {
                root.setChildren(getChilren(root, all));
            }
            return sysDistrictVoList;
        }
        return null;
    }

    @Override
    public List<CameraBaseVo> findByArea(Integer areaId) {
        LambdaQueryWrapper<CameraBase> lambda = new QueryWrapper<CameraBase>().lambda();
        lambda.eq(CameraBase::getDistrictId, areaId).eq(CameraBase::getStatus, 1);
        List<CameraBase> cameraBases = this.list(lambda);
        List<Integer> cameraIds = cameraBases.stream().map(CameraBase::getId).collect(Collectors.toList());

        LambdaQueryWrapper<CameraPlatform> wrapper = new QueryWrapper<CameraPlatform>().lambda();
        wrapper
                .eq(CameraPlatform::getStatus, 1)
                .eq(CameraPlatform::getType, 1)
                .eq(CameraPlatform::getPlatformName, "BTDL")
                .in(cameraIds.size() > 0, CameraPlatform::getOwnId, cameraIds);

        List<CameraPlatform> platforms = cameraPlatformService.list(wrapper);
        List<CameraBaseVo> list = new ArrayList<>();
        cameraBases.forEach(item -> {
            CameraBaseVo cameraBaseVo = new CameraBaseVo();
            BeanUtil.copyProperties(item, cameraBaseVo);
            list.add(cameraBaseVo);
        });

        //查询list和platforms中的id和OwnId相同的数据并给list的每一个数据设置PlatformCameraCode和Url
        platforms.forEach(item -> {
            list.forEach(cameraBaseVo -> {
                if (item.getOwnId().equals(cameraBaseVo.getId())) {
                    cameraBaseVo.setPlatformCameraCode(item.getPlatformCameraCode());
                    cameraBaseVo.setUrl(getUrl(cameraBaseVo.getUserName(), cameraBaseVo.getPassword(), cameraBaseVo.getIp()));
                }
            });
        });
        return list;
    }


    @Override
    public String syncDeviceInfoToBtdlWithPassword() {
        //1. 查询设备扩展和设备基础信息
        List<CameraBase> cameraPlatformWithCameraBase = this.baseMapper.findCameraPlatformWithCameraBase();

        //2. List<CameraBase>转为List<CameraBaseWithPlatformDto>
        List<CameraBaseWithPlatformDto> cameraBaseWithPlatformDtos = cameraPlatformWithCameraBase
                .stream()
                .map(item -> BeanCopyUtils.copy(item, CameraBaseWithPlatformDto.class))
                .collect(Collectors.toList());

        //4. 筛选出cameraBaseWithPlatformDtos中密码为空的
        List<CameraBaseWithPlatformDto> cameraBaseWithPlatformDtos1 = cameraBaseWithPlatformDtos
                .stream()
                .filter(item -> StrUtil.isEmpty(item.getPassword())).collect(Collectors.toList());

        //5. 筛选出cameraBaseWithPlatformDtos中密码不为空的
        List<CameraBaseWithPlatformDto> cameraBaseWithPlatformDtos2 = cameraBaseWithPlatformDtos
                .stream()
                .filter(item -> !StrUtil.isEmpty(item.getPassword())).collect(Collectors.toList());

        //6. 发送密码不为空的到MQTT消息
        if (cameraBaseWithPlatformDtos2.size() > 0) {
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("OpType", "SELECT_BTDL_DEVICE");
            jsonObject.put("Data", cameraBaseWithPlatformDtos2);
            MqttOp.publish(MediaMqConstants.CAMERA_TOPIC, JSON.toJSONString(jsonObject));
        }

        //7. 收集cameraBaseWithPlatformDtos1中name,并去重
        List<String> names = cameraBaseWithPlatformDtos1
                .stream()
                .map(CameraBaseWithPlatformDto::getName)
                .distinct()
                .collect(Collectors.toList());
        //返回names+‘密码为空不能同步’
        return names + "密码为空不能同步";
    }

    @Override
    public List<Tree<Integer>> buildCameraDistrictListTree(Integer districtId, String name) {
        //1. 查询区域设备信息
        List<CameraDistrictVo> cameraDistrictVos = this.baseMapper.selectCameraDistrictList(districtId, name, "BTDL");
        //设置videoUrl
        cameraDistrictVos.forEach(item -> {
            item.setVideoUrl(getUrl(item.getUserName(), item.getPassword(), item.getIp()));
        });
        if (districtId == null || districtId == 0) {
            //2. 封装一个CameraDistrictVo最上层父类
            CameraDistrictVo cameraDistrictVo = new CameraDistrictVo();
            cameraDistrictVo.setId(0);
            cameraDistrictVo.setName("区域");
            cameraDistrictVo.setParentId(-1);
            cameraDistrictVo.setType(1);
            cameraDistrictVo.setSort(0);
            cameraDistrictVos.add(0, cameraDistrictVo);
        }
        //3.判端cameraDistrictVos是否为NULL,为null直接返回CollUtil.newArrayList()
        if (cameraDistrictVos.isEmpty()) {
            return CollUtil.newArrayList();
        }

        //4. 使用TreeBuildUtils.build封装Tree<Integer>
        return TreeBuildUtils.build(cameraDistrictVos, (v, tree) -> {
            tree.setId(v.getId());
            tree.setParentId(v.getParentId());
            tree.setName(v.getName());
            tree.setWeight(-v.getSort());
            tree.put("type", v.getType());
            tree.put("cameraId", v.getCameraId());
            tree.put("disabled", v.getType() == 1);
            tree.put("videoUrl", v.getVideoUrl());
            tree.put("platformCameraCode", v.getPlatformCameraCode());
        });
    }

    @Override
    public String findCameraNameOrCodeById(String deviceIds, Integer type) {
        if (StrUtil.isBlank(deviceIds)) {
            return null;
        }
        //获取设备ID
        List<String> deviceIdList = Arrays.asList(deviceIds.split(","));
        List<JSONObject> jsonObjectList = this.baseMapper.findCameraNameOrCodeById(deviceIdList);
        //根据type判断返回的字段
        if (type == 1) {
            //,拼接jsonObjectList中设备名称
            return jsonObjectList.stream().map(item -> item.getString("NAME")).collect(Collectors.joining(","));
        } else {
            //,拼接jsonObjectList中设备编码
            return jsonObjectList.stream().map(item -> item.getString("CODE")).collect(Collectors.joining(","));
        }
    }

    private String getUrl(String userName, String password, String ip) {
        String url = "rtsp://%s:%s@%s:554";
        return String.format(url, AesUtils.aesDeCode(userName), AesUtils.aesDeCode(password), ip);
    }


    public void setArea(List<CameraBaseVo> records, List<DistrictFeignDto> allArea) {
        for (CameraBaseVo cameraBaseVo : records) {
            cameraBaseVo.setDistrictName(FeignInSystemUtils.completeRoute(cameraBaseVo.getDistrictId(), allArea));
        }
    }


    /**
     * 转化为返回前端得数据
     *
     * @param remoteAllArea 远程数据
     * @param all           需要得数据
     */
    private void convertData(List<DistrictFeignDto> remoteAllArea, List<SysDistrictVo> all) {
        //查询所有通道数据
        List<CameraChannel> cameraChannelList = cameraChannelMapper.selectList(null);
        //查询所有设备数据
        List<CameraBase> cameraBaseList = this.list();

        for (DistrictFeignDto districtFeignDTO : remoteAllArea) {
            SysDistrictVo districtVo = new SysDistrictVo();
            //设置每个区域设备数量
            districtVo.setDevicesNumber(getAreaCameraNumber(cameraBaseList, districtFeignDTO.getId()));
            districtVo.setChannelNumber(getAreaChannelNumber(cameraBaseList, districtFeignDTO.getId(), cameraChannelList));
            //属性赋值
            BeanUtil.copyProperties(districtFeignDTO, districtVo);
            //添加到区域列表
            all.add(districtVo);
        }
    }

    private int getAreaChannelNumber(List<CameraBase> allCamera, Integer districtId, List<CameraChannel> allChannel) {
        //查询区域下得所有设备
        List<CameraBase> list = allCamera.stream().filter(item -> districtId.equals(item.getDistrictId())).collect(Collectors.toList());
        Set<Integer> cameraBaseIds = list.stream().map(CameraBase::getId).collect(Collectors.toSet());
        //2. 获取设备相关所有通道
        List<CameraChannel> cameraChannelList = allChannel.stream().filter(item -> cameraBaseIds.contains(item.getCameraId())).collect(Collectors.toList());
        return cameraChannelList.size();
    }


    /**
     * 获取区域下设备数量
     *
     * @param districtId 区域ID
     * @return int
     */

    private int getAreaCameraNumber(List<CameraBase> all, Integer districtId) {
        List<CameraBase> list = all.stream().filter(item -> districtId.equals(item.getDistrictId())).collect(Collectors.toList());
        return list.size();
    }

    private List<SysDistrictVo> getChilren(SysDistrictVo root, List<SysDistrictVo> all) {
        List<SysDistrictVo> children = new ArrayList<>();
        for (SysDistrictVo vo : all) {
            if (root.getId().equals(vo.getParentId())) {
                vo.setChildren(getChilren(vo, all));
                root.setDevicesNumber(root.getDevicesNumber() + vo.getDevicesNumber());
                root.setChannelNumber(root.getChannelNumber() + vo.getChannelNumber());
                children.add(vo);
            }
        }
        return children;
    }
}

